using System;
using System.Collections.Generic;
using System.Text;

namespace SmartMathLibrary
{
    /// <summary>
    /// Definition of a complex mathematic number. 
    /// </summary>
    [Serializable]
    public class ComplexNumber : IComplexNumber
    {
        /// <summary>
        /// Describes the real part of a number
        /// </summary>
        private double realNumberPart;

        /// <summary>
        /// Describes the imaginary part of a number
        /// </summary>
        private double imaginaryNumberPart;

        /// <summary>
        /// Initializes a new instance of the <see cref="ComplexNumber"/> class.
        /// </summary>
        public ComplexNumber()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ComplexNumber"/> class.
        /// </summary>
        /// <param name="realNumberPart">The real number part.</param>
        public ComplexNumber(double realNumberPart)
        {
            this.realNumberPart = realNumberPart;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ComplexNumber"/> class.
        /// </summary>
        /// <param name="realNumberPart">The real number part.</param>
        /// <param name="imaginaryNumberPart">The imaginary number part.</param>
        public ComplexNumber(double realNumberPart, double imaginaryNumberPart)
        {
            this.realNumberPart = realNumberPart;
            this.imaginaryNumberPart = imaginaryNumberPart;
        }

        /// <summary>
        /// Gets or sets the real number part.
        /// </summary>
        /// <value>The real number part.</value>
        public double RealNumberPart
        {
            get { return realNumberPart; }
            set { realNumberPart = value; }
        }

        /// <summary>
        /// Gets or sets the imaginary number part.
        /// </summary>
        /// <value>The imaginary number part.</value>
        public double ImaginaryNumberPart
        {
            get { return imaginaryNumberPart; }
            set { imaginaryNumberPart = value; }
        }

        /// <summary>
        /// Gets the magnitude of the complex number.
        /// </summary>
        /// <value>The magnitude of the complex number.</value>
        public double Abs
        {
            get
            {
                double a = this.realNumberPart;
                double b = this.imaginaryNumberPart;

                return Math.Sqrt((Math.Pow(a, 2) + Math.Pow(b, 2)));
            }
        }

        /// <summary>
        /// Calculates the modulus of the complex number in polar form.
        /// </summary>
        /// <returns>The modulus of the complex number in polar form.</returns>
        public Double PolarFormModulus
        {
            get
            {
                double a = this.realNumberPart;
                double b = this.imaginaryNumberPart;

                return Math.Sqrt((Math.Pow(a, 2) + Math.Pow(b, 2)));
            }
        }

        /// <summary>
        /// Calculates the angle in degrees for the complex number in polar form.
        /// </summary>
        /// <returns>The angle in degrees for the complex number in polar form.</returns>
        public Double PolarFormAngle
        {
            get { return RadToDeg(Math.Atan2(this.imaginaryNumberPart, this.realNumberPart)); }
        }

        /// <summary>
        /// Gets the argument of the complex number.
        /// </summary>
        /// <value>The argument of the complex number.</value>
        public double Argument
        {
            get
            {
                return Math.Atan2(this.ImaginaryNumberPart, this.RealNumberPart);
            }
        }

        /// <summary>
        /// Inverts the sign of the imaginary number part.
        /// </summary>
        public void InvertImaginaryNumberPart()
        {
            this.imaginaryNumberPart = -this.imaginaryNumberPart;
        }

        /// <summary>
        /// Inverts the sign of the real number part.
        /// </summary>
        public void InvertRealNumberPart()
        {
            this.realNumberPart = -this.realNumberPart;
        }

        /// <summary>
        /// Inverts the sign of the realnumber and imaginary number part.
        /// </summary>
        public void Invert()
        {
            this.InvertRealNumberPart();
            this.InvertImaginaryNumberPart();
        }

        /// <summary>
        /// Calculates the inverse of the current complex number.
        /// </summary>
        /// <returns>The inverse of the current complex number.</returns>
        public ComplexNumber CalculateInverse()
        {
            double x = this.realNumberPart / (Math.Pow(this.realNumberPart, 2) + Math.Pow(this.imaginaryNumberPart, 2));
            double y = -this.imaginaryNumberPart /
                       (Math.Pow(this.realNumberPart, 2) + Math.Pow(this.imaginaryNumberPart, 2));

            return new ComplexNumber(x, y);
        }

        /// <summary>
        /// Converts an angle from radian to degree.
        /// </summary>
        /// <param name="rad">The radian value.</param>
        /// <returns>The converted angle.</returns>
        private static Double RadToDeg(double rad)
        {
            return 180 / Math.PI * rad;
        }

        /// <summary>
        /// Converts an angle from degree to radian.
        /// </summary>
        /// <param name="deg">The degree value.</param>
        /// <returns>The converted angle.</returns>
        private static Double DegToRad(double deg)
        {
            return deg / 180 * Math.PI;
        }

        /// <summary>
        /// Implements the operator +.
        /// </summary>
        /// <param name="number">The number.</param>
        /// <param name="add">The number which should be added.</param>
        /// <returns>The result of the operator.</returns>
        public static ComplexNumber operator +(ComplexNumber number, ComplexNumber add)
        {
            if (number == (ComplexNumber) null)
            {
                throw new ArgumentNullException("number");
            }

            if (add == (ComplexNumber) null)
            {
                throw new ArgumentNullException("add");
            }

            ComplexNumber result = new ComplexNumber();

            result.realNumberPart = number.realNumberPart + add.realNumberPart;
            result.imaginaryNumberPart = number.ImaginaryNumberPart + add.ImaginaryNumberPart;

            return result;
        }

        /// <summary>
        /// Implements the operator +.
        /// </summary>
        /// <param name="number">The number.</param>
        /// <param name="add">The number which should be added.</param>
        /// <returns>The result of the operator.</returns>
        public static ComplexNumber operator +(ComplexNumber number, double add)
        {
            if (number == (ComplexNumber) null)
            {
                throw new ArgumentNullException("number");
            }

            ComplexNumber tempuri = new ComplexNumber(add);

            return number + tempuri;
        }

        /// <summary>
        /// Implements the operator -.
        /// </summary>
        /// <param name="number">The number.</param>
        /// <param name="sub">The number which should be subtracted.</param>
        /// <returns>The result of the operator.</returns>
        public static ComplexNumber operator -(ComplexNumber number, ComplexNumber sub)
        {
            if (number == (ComplexNumber) null)
            {
                throw new ArgumentNullException("number");
            }

            if (sub == (ComplexNumber) null)
            {
                throw new ArgumentNullException("sub");
            }

            ComplexNumber result = new ComplexNumber();

            result.realNumberPart = number.realNumberPart - sub.realNumberPart;
            result.imaginaryNumberPart = number.ImaginaryNumberPart - sub.ImaginaryNumberPart;

            return result;
        }

        /// <summary>
        /// Implements the operator -.
        /// </summary>
        /// <param name="number">The number.</param>
        /// <param name="sub">The number which should be subtracted.</param>
        /// <returns>The result of the operator.</returns>
        public static ComplexNumber operator -(ComplexNumber number, double sub)
        {
            if (number == (ComplexNumber) null)
            {
                throw new ArgumentNullException("number");
            }

            ComplexNumber tempuri = new ComplexNumber(sub);

            return number - tempuri;
        }

        /// <summary>
        /// Implements the operator -.
        /// </summary>
        /// <param name="number">The complex number for sign invertation.</param>
        /// <returns>The result of the operator.</returns>
        public static ComplexNumber operator -(ComplexNumber number )
        {
            if (number == (ComplexNumber)null)
            {
                throw new ArgumentNullException("number");
            }

            return new ComplexNumber(-number.RealNumberPart,-number.ImaginaryNumberPart);
        }

        /// <summary>
        /// Implements the operator *.
        /// </summary>
        /// <param name="number">The number.</param>
        /// <param name="multiply">The number to multiply.</param>
        /// <returns>The result of the operator.</returns>
        public static ComplexNumber operator *(ComplexNumber number, ComplexNumber multiply)
        {
            if (number == (ComplexNumber) null)
            {
                throw new ArgumentNullException("number");
            }

            if (multiply == (ComplexNumber) null)
            {
                throw new ArgumentNullException("multiply");
            }

            ComplexNumber result = new ComplexNumber();

            result.realNumberPart = (number.RealNumberPart * multiply.RealNumberPart) -
                                    (number.ImaginaryNumberPart * multiply.ImaginaryNumberPart);
            result.imaginaryNumberPart = (number.RealNumberPart * multiply.ImaginaryNumberPart) +
                                         (number.ImaginaryNumberPart * multiply.RealNumberPart);

            return result;
        }

        /// <summary>
        /// Implements the operator *.
        /// </summary>
        /// <param name="number">The number.</param>
        /// <param name="multiply">The number to multiply.</param>
        /// <returns>The result of the operator.</returns>
        public static ComplexNumber operator *(ComplexNumber number, double multiply)
        {
            if (number == (ComplexNumber) null)
            {
                throw new ArgumentNullException("number");
            }

            ComplexNumber tempuri = new ComplexNumber(multiply);

            return number * tempuri;
        }

        /// <summary>
        /// Implements the operator *.
        /// </summary>
        /// <param name="multiply">The number to multiply.</param>
        /// <param name="number">The number.</param>
        /// <returns>The result of the operator.</returns>
        public static ComplexNumber operator *( double multiply, ComplexNumber number)
        {
            if (number == (ComplexNumber)null)
            {
                throw new ArgumentNullException("number");
            }

            ComplexNumber tempuri = new ComplexNumber(multiply);

            return number * tempuri;
        }

        /// <summary>
        /// Implements the operator /.
        /// </summary>
        /// <param name="number">The number.</param>
        /// <param name="divide">The number to divide.</param>
        /// <returns>The result of the operator.</returns>
        public static ComplexNumber operator /(ComplexNumber number, ComplexNumber divide)
        {
            if (number == (ComplexNumber) null)
            {
                throw new ArgumentNullException("number");
            }

            if (divide == (ComplexNumber) null)
            {
                throw new ArgumentNullException("divide");
            }

            double a = number.RealNumberPart;
            double b = number.ImaginaryNumberPart;
            double c = divide.RealNumberPart;
            double d = divide.ImaginaryNumberPart;

            if ((c == 0) && (d == 0))
            {
                throw new DivideByZeroException("The divisor cannot be zero.");
            }

            ComplexNumber result = new ComplexNumber();

            result.RealNumberPart = ((a * c) + (b * d)) / (Math.Pow(c, 2) + Math.Pow(d, 2));
            result.ImaginaryNumberPart = ((b * c) - (a * d)) / (Math.Pow(c, 2) + Math.Pow(d, 2));

            return result;
        }

        /// <summary>
        /// Implements the operator /.
        /// </summary>
        /// <param name="number">The number.</param>
        /// <param name="divide">The number to divide.</param>
        /// <returns>The result of the operator.</returns>
        public static ComplexNumber operator /(ComplexNumber number, double divide)
        {
            if (number == (ComplexNumber) null)
            {
                throw new ArgumentNullException("number");
            }

            ComplexNumber tempuri = new ComplexNumber(divide);

            return number / tempuri;
        }

        /// <summary>
        /// Implements the operator /.
        /// </summary>
      /// <param name="divide">The number to divide.</param>
        /// <param name="number">The number.</param>
        /// <returns>The result of the operator.</returns>
        public static ComplexNumber operator /(double divide,ComplexNumber number)
        {
            if (number == (ComplexNumber)null)
            {
                throw new ArgumentNullException("number");
            }

            ComplexNumber tempuri = new ComplexNumber(divide);

            return tempuri / number;
        }

        /// <summary>
        /// Implements the operator ==.
        /// </summary>
        /// <param name="a">The first complex number.</param>
        /// <param name="b">The second complex number.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator ==(ComplexNumber a, ComplexNumber b)
        {
            if (System.Object.ReferenceEquals(a, b))
            {
                return true;
            }

            if (((object) a == null) || ((object) b == null))
            {
                return false;
            }

            return ((a.RealNumberPart == b.RealNumberPart) && (a.ImaginaryNumberPart == b.ImaginaryNumberPart));
        }

        /// <summary>
        /// Implements the operator ==.
        /// </summary>
        /// <param name="a">The first complex number.</param>
        /// <param name="b">The second complex number.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator !=(ComplexNumber a, ComplexNumber b)
        {
            return !(a == b);
        }

        /// <summary>
        /// Powers the current complex number raised to the specified power.
        /// </summary>
        /// <param name="n">A number that specifies a power.</param>
        public void Pow(double n)
        {
            double rn = Math.Pow(this.PolarFormModulus, n);
            double phi = DegToRad(n * this.PolarFormAngle);

            this.realNumberPart = rn * Math.Cos(phi);
            this.imaginaryNumberPart = rn * Math.Sin(phi);
        }

        /// <summary>
        /// Transforms a complex number from the polar form to the cartesian form(a + bi).
        /// </summary>
        /// <param name="r">The modulus of the number, normally r.</param>
        /// <param name="phi">The angle of the complex number, normally phi.</param>
        /// <returns>The complex number in cartesian form.</returns>
        public ComplexNumber PolarToCartesian(double r, double phi)
        {
            ComplexNumber result = new ComplexNumber();

            result.RealNumberPart = r * Math.Cos(DegToRad(phi));
            result.ImaginaryNumberPart = r * Math.Sin(DegToRad(phi));

            return result;
        }

        /// <summary>
        /// Parses the complex number in form of a + bi.
        /// </summary>
        /// <param name="s">The complex number to parse.</param>
        /// <returns>The parsed complex number.</returns>
        public static ComplexNumber ParseComplexNumber(String s)
        {
            if (s == (string) null)
            {
                throw new ArgumentNullException("s");
            }
            if (
                1 + ((String) s).IndexOf("+") < 0 || s.Length < 1 + ((String) s).IndexOf("+") ||
                -1 + ((String) s).IndexOf("i") - ((String) s).IndexOf("+") < 0 ||
                s.Length < ((String) s).IndexOf("i"))
            {
                throw new ArgumentException("complex reason", "s");
            }

            ComplexNumber result = new ComplexNumber();

            if ((s.IndexOf("+") > 0) && (s.Trim().EndsWith("i", StringComparison.OrdinalIgnoreCase)))
            {
                result.RealNumberPart = Double.Parse(s.Substring(0, s.IndexOf("+")).Trim());
                result.ImaginaryNumberPart =
                    Double.Parse(s.Substring(s.IndexOf("+") + 1, s.IndexOf("i") - (s.IndexOf("+") + 1)).Trim());

                return result;
            }
            throw new ArgumentException("The argument cannot be identify as a complex number.");
        }

        /// <summary>
        /// Copies the current instance.
        /// </summary>
        /// <returns>A copy of the current instance.</returns>
        public ComplexNumber Copy()
        {
            return new ComplexNumber(this.realNumberPart, this.imaginaryNumberPart);
        }

        /// <summary>
        /// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </returns>
        public override string ToString()
        {
            String result = this.realNumberPart.ToString();

            if (this.imaginaryNumberPart < 0)
            {
                result += " - " + (-this.imaginaryNumberPart).ToString() + "i";
            }
            else
            {
                result += " + " + this.imaginaryNumberPart.ToString() + "i";
            }

            return result;
        }

        /// <summary>
        /// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
        /// <returns>
        /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
        /// </returns>
        /// <exception cref="T:System.NullReferenceException">The <paramref name="obj"/> parameter is null.</exception>
        public override bool Equals(object obj)
        {
            return base.Equals(obj);
        }

        /// <summary>
        /// Serves as a hash function for a particular type.
        /// </summary>
        /// <returns>
        /// A hash code for the current <see cref="T:System.Object"/>.
        /// </returns>
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }
    }
}